/*************************************************************************************
 *
 *	模块名称 : UDP收发模块
 *	文件名称 : myudp.c
 *	版   本 : V1.0
 *	说   明 : UDP服务器和客户端的初始化
 　　　　　　　 UDP收发数据
             UDP收发文件 
 *	修改记录 :
 *		     版本号       日期         作者        说明
 *	         V1.0     2022-06-17      YT        发布
 *
 *	Copyright (C), 2022-2030, Zhirui
 *
 *************************************************************************************/

#include "myudp.h"

int UdpSelfBind( int *pSockFd, const char strSelfIp[], int UdpSelfPort )
{
    int Ret;
    
    *pSockFd = socket( AF_INET, SOCK_DGRAM, 0 );
    if( -1 == *pSockFd )
    {
        perror( "fail UdpSelfBind" );
        return -1;
    }

    if ( ( strSelfIp != NULL ) && ( UdpSelfPort != -1 ) )
    {
        struct sockaddr_in SelfAddr;
        SelfAddr.sin_family = AF_INET;
        SelfAddr.sin_port = htons( UdpSelfPort );
        SelfAddr.sin_addr.s_addr = inet_addr( strSelfIp );

        Ret = bind( *pSockFd, (struct sockaddr*)&SelfAddr, sizeof(SelfAddr) );
        if ( -1 == Ret )
        {
            perror( "fail bind @ UdpSeverSelfInit" );
            return -1;
        }
    }

    return 0;
}

ssize_t UdpSendto( int SockFd, const void *SendBuff, size_t SizeSend, const char strClintIp[], int ClintPort )
{
    struct sockaddr_in ClintAddr;
    ClintAddr.sin_family      = AF_INET;
    ClintAddr.sin_port        = ntohs( ClintPort );
    ClintAddr.sin_addr.s_addr = inet_addr( strClintIp );

    ssize_t sizeRet = sendto( SockFd, SendBuff, SizeSend, 0, 
                            (struct sockaddr*)&ClintAddr, sizeof(ClintAddr) );
    if ( -1 == sizeRet )
    {
        perror( "fail sendto @ UdpSendto" );
        return -1;
    }

    return sizeRet;
}

ssize_t UdpRecvfrom( int SockFd, void *RecvBuff, size_t SizeRecv, char strClintIp[], int *pClintPort )
{
    struct sockaddr_in ClintAddr;
    socklen_t ClintAddrLen = sizeof(ClintAddr);

    ssize_t SizeRet;
    SizeRet = recvfrom( SockFd, RecvBuff, SizeRecv, 0, 
                        (struct sockaddr *)&ClintAddr, &ClintAddrLen );
    if ( -1 == SizeRet )
    {
        perror( "fail recvfrom @ UdpServerRecvfromClint" );
        return -1;
    }
    
    if( strClintIp != NULL )
    {
        strcpy( strClintIp, inet_ntoa(ClintAddr.sin_addr) );
    }

    if( pClintPort != NULL )
    {
        *pClintPort = ntohs(ClintAddr.sin_port);
    }

    return SizeRet;
}


int  UdpSendFile( int SockFd, const char SendFileName[], const char strClintIp[], int ClintPort )
{
    FILE* FdSendFile = fopen( SendFileName, "r" );
    fseek( FdSendFile, 0, SEEK_END );
    long CrtFileLen = ftell( FdSendFile );
    rewind( FdSendFile );

    // 首先, 发送 文件索引 UdpFileIndex 结构体 到 指定的 UDP服务器
    UdpFileIndex UdpFile;
    strcpy( UdpFile.FileName, SendFileName );
    UdpFile.FileLen = CrtFileLen;

    int Ret;
    Ret = UdpSendto( SockFd, &UdpFile, sizeof(UdpFile), strClintIp, ClintPort );
    if( -1 == Ret )
    {
        perror( "fail UdpSendto @ UdpSendFile" );
        return -1;
    }

    // 其次, 发送文件内容
    char SendBuff[256] =  {0};
    size_t RetFread;
    while ( ( RetFread = fread( SendBuff, 1, sizeof(SendBuff), FdSendFile ) ) != 0 )
    {
        Ret = UdpSendto( SockFd, SendBuff, RetFread, strClintIp, ClintPort );
        if( -1 == Ret )
        {
            perror( "fail UdpSendto @ UdpSendFile" );
            return -1;
        }
    }

    if( -1 == RetFread )
    {
        perror( "fail fread @ UdpSendFile" );
        return -1;
    }

    Ret = fclose( FdSendFile );
    if( -1 == Ret )
    {
        perror( "fail flcose @ UdpSendFile");
    }

    return 0;
}

int UdpRecvFile( int SockFd, char RecvFileName[], char strClintIp[], int *pClintPort )
{
    //首先 接收 文件索引 UdpFileIndex结构体
    UdpFileIndex UdpRecvFileIndex;
    ssize_t SizeRet;
    char strRecvIndexClintIp[50] = {0};
    int RecvIndexClintPort = 0;

    SizeRet = UdpRecvfrom( SockFd, &UdpRecvFileIndex, sizeof(UdpRecvFileIndex), 
                        strRecvIndexClintIp, &RecvIndexClintPort );
    if ( -1 == SizeRet )
    {
        perror( "fail UdpRecvfrom @ UdpRecvFile" );
        return -1;
    }

    // 如果主调函数希望接收发送方的地址和端口, 则返回给主调函数
    if( strClintIp != NULL )
    {
        strcpy( strClintIp, strRecvIndexClintIp );
    }
    
    if( pClintPort != NULL )
    {
        *pClintPort = RecvIndexClintPort;
    }   

    // 其次 接收文件内容, 并保存到 新文件 中    
    sprintf( RecvFileName, "_UDP_%s", UdpRecvFileIndex.FileName );  //在接收到的文件名加前缀  
    FILE *FdRecvFile = fopen( RecvFileName, "w" );
    if ( NULL == FdRecvFile )
    {
        perror( "fail fopen @ UdpRecvFile" );
        return -1;
    }

    char strRecvContentClintIp[50] = {0};
    int RecvContentClintPort = 0;
    ssize_t SizeRecv = 0;
    int RecvCounter = 0;
    char RecvBuff[1024] = {0};
    int Ret;

    while ( RecvCounter < UdpRecvFileIndex.FileLen )
    {
        SizeRecv = UdpRecvfrom( SockFd, RecvBuff, sizeof(RecvBuff), 
                                strRecvContentClintIp, &RecvContentClintPort );
        if ( -1 == SizeRecv )
        {
            perror( "fail UdpRecvfrom @ UdpRecvFile" );
            return -1;
        }

        // 仅把同样来自同一个IP+端口的数据写入文件, 忽略来自其他IP+端口的数据
        if ( ( strcmp( strRecvContentClintIp, strRecvIndexClintIp ) == 0 )
             && (RecvContentClintPort == RecvIndexClintPort ) )
        {
            Ret = fwrite( RecvBuff, 1, SizeRecv, FdRecvFile );
            if( -1 == Ret )
            {
                perror( "fail fwrite @ UdpRecvFile" );
                return -1;
            }

            RecvCounter += SizeRecv; 
        }    
    }

    Ret = fclose( FdRecvFile );
    if ( -1 == Ret )
    {
        perror( "fail fclose @ UdpRecvFile" );
    }

    return 0;
}
